﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.WowAddonStudio.Lua.LanguageService.Model;
using Microsoft.WowAddonStudio.Lua.Model;
using Microsoft.WowAddonStudio.Lua.Support;

namespace Microsoft.WowAddonStudio.Lua.LanguageService.Intellisense
{
    public static class DescriptionExtensions
    {
        private const string parameterChoiceSeparator = " or ";

        private static readonly Dictionary<Type, IDescriptionProvider> descriptionProviders = InitializeDescriptionProviders();
        private static Dictionary<Type, IDescriptionProvider> InitializeDescriptionProviders()
        {
            return new Dictionary<Type, IDescriptionProvider>
                   {
                       { typeof(Function), new FunctionDescriptionProvider() },
                       { typeof(Table), new TableDescriptionProvider() }
                   };
        }

        private static readonly IDescriptionProvider defaultDescriptionProvider = new DescriptionProvider<Declaration>();

        public static string GetDisplayText(this Declaration declaration)
        {
            return declaration.Name;
        }

        public static string GetName(this Declaration declaration)
        {
            return declaration.Name;
        }

        public static string GetDescription(this Declaration declaration)
        {
            if (descriptionProviders.ContainsKey(declaration.GetType()))
                return descriptionProviders[declaration.GetType()].GetDescription(declaration);

            return defaultDescriptionProvider.GetDescription(declaration);
        }

        public static string GetName(this Function function)
        {
            return function.Name;
        }

        public static string GetMethodDescription(this Function function)
        {
            return function.GetSummary();
        }

        public static string GetReturnType(this Function function)
        {
            return null;
        }

        public static ParameterInfo GetParameterInfo(this Function function, Parameter parameter)
        {
            var parameterInfo = new ParameterInfo();

            if (parameter is ParameterChoice)
            {
                var parameterChoice = (ParameterChoice)parameter;

                var descriptionBuilder = new StringBuilder();

                for (int i = 0; i < parameterChoice.Choices.Length; i++)
                {
                    descriptionBuilder.AppendFormat("{0}: {1}", parameterChoice.Choices[i].GetName(), parameterChoice.Choices[i].GetSummary());

                    if (parameterChoice.Choices.Length - i > 1)
                        descriptionBuilder.AppendLine();
                }

                parameterInfo.Name = 
                parameterInfo.Display = parameterChoice.Choices.Select(p => p.GetName()).Concatenate(parameterChoiceSeparator);
                parameterInfo.Description = descriptionBuilder.ToString();
            }
            else
            {
                parameterInfo.Name =
                parameterInfo.Display = parameter.GetName();
                parameterInfo.Description = parameter.GetSummary();
            }

            return parameterInfo;
        }

        public static string GetSignature(this Function function)
        {
            if (function == null)
                throw new ArgumentNullException("function");

            var signatureBuilder = new StringBuilder();

            signatureBuilder.Append(function.Name);
            signatureBuilder.Append('(');

            bool firstParameter = true;
            int optionalCount = 0;

            if (function.Parameters != null)
            {
                foreach (var parameter in function.Parameters)
                {
                    if (parameter.Optional)
                    {
                        signatureBuilder.Append("[");
                        optionalCount++;
                    }

                    if (!firstParameter)
                        signatureBuilder.Append(", ");

                    signatureBuilder.Append(parameter.GetSignature());
                    firstParameter = false;
                }

                signatureBuilder.Append(']', optionalCount);
            }

            signatureBuilder.Append(')');
            return signatureBuilder.ToString();
        }

        public static string GetSummary(this Declaration declaration)
        {
            if (declaration == null)
                throw new ArgumentNullException("declaration");

            if (!String.IsNullOrEmpty(declaration.Summary))
                return declaration.Summary.Trim();

            return null;
        }


        public static string GetSignature(this Parameter parameter)
        {
            var signatureBuilder = new StringBuilder();
            if (parameter is ParameterChoice)
            {
                foreach (var choice in ((ParameterChoice)parameter).Choices)
                {
                    if (signatureBuilder.Length > 0)
                        signatureBuilder.Append(parameterChoiceSeparator);

                    signatureBuilder.Append(choice.GetSignature());
                }
            }
            else
            {
                signatureBuilder.Append(GetSignature((ApiDeclaration)parameter));
            }
            return signatureBuilder.ToString();
        }

        public static string GetSignature(this ApiDeclaration apiDeclaration)
        {
            var signatureBuilder = new StringBuilder();

            signatureBuilder.Append(apiDeclaration.GetTypeSignature().ToLower());
            if (signatureBuilder.Length > 0)
                signatureBuilder.Append(" ");
            signatureBuilder.Append(apiDeclaration.GetName());

            return signatureBuilder.ToString();
        }


        public static string GetTypeSignature(this ApiDeclaration apiDeclaration)
        {
            if (apiDeclaration.Type == LuaType.Unknown)
                return String.Empty;

            return apiDeclaration.ApiType != ApiType.Unknown ? apiDeclaration.ApiType.ToString() : apiDeclaration.Type.ToString();
        }
    }
}
